<?php
/*
 * desc：
 *
 * author：wh
 * email：
 * createTime：{2022/03/30} {09:59} 
 */

namespace wanghua\general_utility_tools_php\http;

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Utils;
use wanghua\general_utility_tools_php\tool\Tools;

/**
 * desc：cUrl网络库【推荐】
 *
 * author：wh
 * Class Curl
 * @package wanghua\general_utility_tools_php\http
 */
class Curl
{

    /**
     * desc：[推荐] curl请求
     *
     * 如果是文件流，则需要用getContents()方法获取
     * eg: $res->getBody()->getContents();
     *
     * author：wh
     * @param $url
     * @param string $method
     * @param false[] $config
     * @param array $headers
     * @return mixed
     */
    static function request($url,$method='POST',$config=['verify' => false],$headers=[]){
        $client = new Client($config);
        //$headers = [
        //    //'User-Agent' => 'Apifox/1.0.0 (https://apifox.com)',
        //    //'Accept' => '*/*',
        //    //'Host' => 'vits_simple.excn.top',
        //    //'Connection' => 'keep-alive'
        //];
        $request = new Request($method, $url, $headers);
        $res = $client->sendAsync($request)->wait();
        //$res->getBody()->getContents();//如果是文件流，则需要用getContents()方法获取
        return $res->getBody();
    }

    /**
     * desc：php curl远程上传文件，提交到远程服务器，适用于上传多个文件(文件类型不限)
     *
     * 应用场景：
     * 前端->后端A->后端B
     * 说明：前端发起文件上传，后端A接收到文件后，将文件使用guzzlehttp/guzzle上传到后端B，后端B将文件处理
     *
     * 当前版本依赖："guzzlehttp/guzzle": "^7.8"
     *
     * author：wh
     * @param $url 远程地址
     * @param $params 提交参数
     * @param array $config 配置项
     */
    function curlRemoteUploadFiles($url,$params, $config=['verify' => false]){
        // 创建 Guzzle HTTP 客户端实例
        $client = new Client($config);

        // 定义要上传的文件列表
        $files = [
            //[
            //    'name'     => 'file1', // 服务器接收的文件字段名
            //    'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\dc1c441d81d48565ac6817b89d0f8bef.mp4', 'r'), // 文件路径
            //    'filename' => 'dc1c441d81d48565ac6817b89d0f8bef.mp4', // 文件名，可自定义
            //],
            //[
            //    'name'     => 'files',
            //    'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\f80179b23b60619fba8033bfd64f8817.mp4', 'r'),
            //    'filename' => 'f80179b23b60619fba8033bfd64f8817.mp4',
            //],
            //['name'=>'prompt','contents'=>$prompt],
            //['name'=>'tts_url','contents'=>$tts_url]
            // 可以继续添加更多文件...
        ];
        foreach ($params as $key=>$val){
            $files[] = ['name'=>$key,'contents'=>$val];
        }
        $controller = \request()->controller();
        $action = \request()->action();
        $save_path = Tools::get_root_path() . "public/uploads/{$controller}/{$action}/";
        $files_obj = \request()->file('files');
        foreach ($files_obj as $k=>$file) {
            if ($file->check()) {
                $save_res = $file->move($save_path);
                $fileinfo = $file->getInfo();
                // 使用CURLFile包装文件路径以供上传
                $filename = $save_path.date('Ymd').'/'.$save_res->getFilename();
                $files[] = [
                    'name'=>"files",//服务器接收的文件字段名
                    'contents'=>Utils::tryFopen($filename, 'r'),
                    'filename'=>$fileinfo['name'],//可自定义
                ];
            }
            //else {
            //    return json(['code' => -1, 'msg' => '文件上传失败: ' . $file->getError()]);
            //}
        }

        // 构建多部分表单数据
        $multipartStream = new MultipartStream($files);
        $boundary = $multipartStream->getBoundary();
        $headers = [
            'Content-Type' => "multipart/form-data; boundary={$boundary}",
        ];

        // 准备请求体
        $body = $multipartStream->getContents();

        // 发起 POST 请求
        try {
            $response = $client->request('POST', $url, [
                'headers' => $headers,
                'body' => $body,
            ]);

            // 处理响应
            $responseBody = (string) $response->getBody();
            //echo "Server response: " . $responseBody;
            return Tools::set_ok('ok',$responseBody);
        } catch (\GuzzleHttp\Exception\RequestException $e) {
            // 错误处理
            //echo "Error: " . $e->getMessage();
            return Tools::set_fail("Error: " . $e->getMessage());
        }
    }

    /**
     * @deprecated 弃用
     * GET
     *
     * author：wh
     * @param string $url
     * @param int $timeout
     * @return bool|int|string
     */
    static function curl_get(string $url, int $timeout = 10, $header=[])
    {

        $header = $header?:array(
            'Accept: application/json',
        );
        $curl = curl_init();
        //curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        //curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        //curl_setopt($curl, CURLOPT_SSLVERSION, 3);
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_HEADER, 0);
        // 超时设置,以秒为单位
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);

        // 超时设置，以毫秒为单位
        // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

        // 设置请求头
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        //设置获取的信息以文件流的形式返回，而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        //执行命令
        $data = curl_exec($curl);

        // 显示错误信息
        if (curl_error($curl)) {
            //print "Error: ".curl_errno($curl).'-' . curl_error($curl);
            //返回错误码
            return ['code' => curl_errno($curl), 'msg' => curl_error($curl)];
        } else {
            //关闭句柄
            curl_close($curl);
            // 返回的内容
            return ['code' => 200, 'msg' => 'cURL ok', 'data' => $data];
        }
    }
    /**
     * @deprecated 弃用
     * POST 表单
     *
     * author：wh
     * @param string $url 是请求的链接
     * @param array $postdata 传输的数据，数组格式
     * @return bool|int|string
     */
    static function curl_post(string $url, array $postdata, $header=[]) {
        $timeout = 4;
        $connect_timeout = 1;
        $set_time_limit = 5;
        if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
        set_time_limit($set_time_limit);
        $header = $header?:array(
            'Accept: application/json',
        );

        //初始化
        $curl = curl_init();
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_HEADER, 0);
        //设置获取的信息以文件流的形式返回，而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        // 超时设置
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
        //发起连接前等待的时间，如果设置为0，则无限等待。
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);

        // 超时设置，以毫秒为单位
        // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

        // 设置请求头
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );

        //设置post方式提交
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
        //执行命令
        $data = curl_exec($curl);

        // 显示错误信息
        if (curl_error($curl)) {
            //返回错误码
            return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
        } else {
            //关闭句柄
            curl_close($curl);
            // 返回的内容
            return ['code'=>200, 'msg'=>'cURL ok', 'data'=>$data];
        }
    }

    /**
     * @deprecated 弃用
     * POST 查询参数
     *
     * author：wh
     * @param string $url 是请求的链接
     * @param array $postdata 传输的数据，http_build_query查询参数格式
     * @return bool|int|string
     */
    static function curl_post_build_query(string $url, array $postdata, $header=[]) {
        $timeout = 4;
        $connect_timeout = 1;
        $set_time_limit = 5;
        if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
        set_time_limit($set_time_limit);
        $header = $header?:array(
            'Accept: application/json',
        );

        //初始化
        $curl = curl_init();
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_HEADER, 0);
        //设置获取的信息以文件流的形式返回，而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        // 超时设置
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
        //发起连接前等待的时间，如果设置为0，则无限等待。
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);

        // 超时设置，以毫秒为单位
        // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

        // 设置请求头
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );

        //设置post方式提交
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($postdata));
        //执行命令
        $data = curl_exec($curl);

        // 显示错误信息
        if (curl_error($curl)) {
            //返回错误码
            return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
        } else {
            //关闭句柄
            curl_close($curl);
            // 返回的内容
            return ['code'=>200, 'msg'=>'ok', 'data'=>$data];
        }
    }

    /**
     * @deprecated 弃用
     * POST json
     *
     * author：wh
     * @param string $url 是请求的链接
     * @param array $postdata 传输的数据，json格式
     * @return 返回数组格式
     */
    static function curl_post_json(string $url, array $postdata, $header=[]) {
        $timeout = 4;
        $connect_timeout = 1;
        $set_time_limit = 5;
        if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
        set_time_limit($set_time_limit);
        $header = $header?:array(
            'Accept: application/json',
        );

        //初始化
        $curl = curl_init();
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_HEADER, 0);
        //设置获取的信息以文件流的形式返回，而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        // 超时设置
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
        //发起连接前等待的时间，如果设置为0，则无限等待。
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);

        // 超时设置，以毫秒为单位
        // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

        // 设置请求头
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );

        //设置post方式提交
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postdata,JSON_UNESCAPED_UNICODE));
        //执行命令
        $data = curl_exec($curl);

        // 显示错误信息
        if (curl_error($curl)) {
            //返回错误码
            return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
        } else {
            //关闭句柄
            curl_close($curl);
            // 返回的内容
            return ['code'=>200, 'msg'=>'cURL ok', 'data'=>$data];
        }
    }

    /**
     * @deprecated 弃用
     * POST json ,成功直接返回请求结果，错误返回数组
     *
     * 注：此方法不是表单提交
     *
     * author：wh
     * @param string $url 是请求的链接
     * @param array $postdata 传输的数据，最终会转换为json格式请求
     * @return 直接返回请求数据，适合请求第三方返回BUFFER数据流的接口
     */
    static function curl_post_json_return_buffer(string $url, array $postdata, $header=[]) {
        $timeout = 4;
        $connect_timeout = 1;
        $set_time_limit = 5;
        if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
        set_time_limit($set_time_limit);
        $header = $header?:array(
            'Accept: application/json',
        );

        //初始化
        $curl = curl_init();
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_HEADER, 0);
        //设置获取的信息以文件流的形式返回，而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        // 超时设置
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
        //发起连接前等待的时间，如果设置为0，则无限等待。
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);

        // 超时设置，以毫秒为单位
        // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

        // 设置请求头
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );

        //设置post方式提交
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postdata,JSON_UNESCAPED_UNICODE));
        //执行命令
        $data = curl_exec($curl);

        // 显示错误信息
        if (curl_error($curl)) {
            //返回错误码
            return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
        } else {
            //关闭句柄
            curl_close($curl);
            // 返回的内容
            return $data;
        }
    }

    /**
     * @deprecated 弃用
     * POST [流]
     *
     * [请求Java接口]
     *
     * 思考：1、Java端如果用文件流的方式去获取数据，调用此方法
     *
     * @param $url
     * @param $postdata
     * @param int $timeout
     * @return array
     * @link
     * @example
     * @see
     */
    static function java_curl_post_file_request($url, $postdata, $timeout = 10, $header=[])
    {

        $header = $header?:array(
            'Accept: application/json',
        );

        //初始化
        $curl = curl_init();
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
        curl_setopt($curl, CURLOPT_HEADER, 0);
        //设置获取的信息以文件流的形式返回，而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        // 超时设置
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);

        // 超时设置，以毫秒为单位
        // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

        // 设置请求头
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);

        //设置post方式提交
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
        //执行命令
        $data = curl_exec($curl);

        // 显示错误信息
        if (curl_error($curl)) {
            //返回错误码
            return ['code' => curl_errno($curl), 'msg' => curl_error($curl)];
        } else {
            //关闭句柄
            curl_close($curl);
            // 返回的内容
            return ['code' => 200, 'msg' => 'ok', 'data' => $data];
        }
    }

    /**
     * @deprecated 弃用
     * 统一请求 GET/POST请求
     *
     * 注：此设置允许header重定向，适合部分请求头中携带参数的接口，如请求头携带token
     *
     * @param String $url 接口地址
     */
    static function curl_request($url, $method = 'GET',$data=null,$header=array(),$call_back=null)
    {
        set_time_limit(30);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        if($header){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        }
        if($method == 'POST'){
            if($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        if($call_back){
            //使用此特性前除非你清楚理解回调函数，否则不推荐
            curl_setopt($ch, CURLOPT_WRITEFUNCTION, $call_back);
        }
        $result = curl_exec($ch);
        if (curl_errno($ch)) {
            return [
                'status' => 'error',
                'message' => 'curl 错误信息: ' . curl_error($ch)
            ];
        }
        curl_close($ch);
        return $result;
    }


    /**
     * @deprecated 弃用
     * desc：php curl模拟文件上传
     * post
     * author：wh
     * @param $url 提交地址
     * @param $params 表单参数
     * @param $files 上传文件
     * @param $header 请求头
     * @return array
     */
    static function curlFileUpload($url,$params,$files,$header)
    {
        // 初始化cURL会话
        $ch = curl_init();
        $header = $header ?: array(
            'Accept: application/json',
            'Content-Type: multipart/form-data'
        );
        // 合并文件和其他字段数据
        $data = array_merge($params, $files);

        // 设置cURL选项
        curl_setopt($ch, CURLOPT_URL, $url);
        // 设置请求头
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

        // 执行cURL请求
        $result = curl_exec($ch);
        if (curl_errno($ch)) {
            return ['code' => curl_errno($ch), 'msg' => curl_error($ch)];
        }
        //关闭句柄
        curl_close($ch);
        // 返回的内容
        return ['code' => 200, 'msg' => 'cURL ok', 'data' => $result];
    }

}